#include "buf_pool.h"
#include <assert.h>


//单例对象
buf_pool * buf_pool::_instance = NULL;

//用于保证创建单例的init方法只执行一次的锁
pthread_once_t buf_pool::_once = PTHREAD_ONCE_INIT;


//用户保护内存池链表修改的互斥锁
pthread_mutex_t buf_pool::_mutex = PTHREAD_MUTEX_INITIALIZER;

void buf_pool::make_io_buf_list(int cap, int num)
{
    //链表的头指针
    io_buf *prev;

    //开辟4k buf内存池 
    _pool[cap] = new io_buf(cap);
    if (_pool[cap] == NULL) {
        fprintf(stderr, "new io_buf %d error", cap);
        exit(1);
    }

    prev = _pool[cap];

    for (int i = 1; i < num; i++) {
        prev->next = new io_buf(cap);
        if (prev->next == NULL) {
            fprintf(stderr, "new io_buf cap error");
            exit(1);
        }
        prev = prev->next;
    }

    _total_mem += cap/1024 * num;    
}

//构造函数 主要是预先开辟一定量的空间
//这里buf_pool是一个hash，每个key都是不同空间容量
//对应的value是一个io_buf集合的链表
//buf_pool -->  [m4K]   -- 5000个
//              [m16K]  -- 1000个
//              [m64K]  -- 500个
//              [m256K] -- 200个
//              [m1M]   -- 50个
//              [m4M]   -- 20个
//              [m8M]   -- 10个
buf_pool::buf_pool():_total_mem(0)
{
    make_io_buf_list(m4K, 5000);
    make_io_buf_list(m16K, 1000);
    make_io_buf_list(m64K, 500);
    make_io_buf_list(m256K,200);
    make_io_buf_list(m1M, 50);
    make_io_buf_list(m4M, 20);
    make_io_buf_list(m8M, 10);
}


//开辟一个io_buf
//1 如果上层需要N个字节的大小的空间，找到与N最接近的buf hash组，取出，
//2 如果该组已经没有节点使用，可以额外申请
//3 总申请长度不能够超过最大的限制大小 EXTRA_MEM_LIMIT
//4 如果有该节点需要的内存块，直接取出，并且将该内存块从pool摘除
io_buf *buf_pool::alloc_buf(int N) 
{

    //1 找到N最接近哪hash 组
    int index;
    if (N <= m4K) {
        index = m4K;
    }
    else if (N <= m16K) {
        index = m16K;
    }
    else if (N <= m64K) {
        index = m64K;
    }
    else if (N <= m256K) {
        index = m256K;
    }
    else if (N <= m1M) {
        index = m1M;
    }
    else if (N <= m4M) {
        index = m4M;
    }
    else if (N <= m8M) {
        index = m8M;
    }
    else {
        return NULL;
    }

    //因为需要对内存处理，所以需要加锁
    pthread_mutex_lock(&_mutex);

    //2 如果该组已经没有，需要额外申请
    if (_pool[index] == NULL) {
        if (_total_mem + index/1024 >= EXTRA_MEM_LIMIT) {
            //当前的开辟的空间已经超过最大限制
            fprintf(stderr, "already use too many memory!\n");
            exit(1);
        }

        io_buf *new_buf = new io_buf(index);
        if (new_buf == NULL) {
            fprintf(stderr, "new io_buf error\n");
            exit(1);
        }
        _total_mem += index/1024;
        pthread_mutex_unlock(&_mutex);
        return new_buf;
    }

    //3 从pool中摘除该内存块
    io_buf *target = _pool[index];
    _pool[index] = target->next;

    pthread_mutex_unlock(&_mutex);
    target->next = NULL;

    return target;
}

// 如果没有给分配的大小，则默认m4K
io_buf *buf_pool::alloc_buf()
{
    return alloc_buf(m4K);
}

//重置一个io_buf,将一个buf 上层不再使用，或者使用完成之后，需要将该buf放回pool中
void buf_pool::revert(io_buf *buffer)
{
    //每个buf的容量都是固定的 在hash的key中取值
    int index = buffer->capacity;

    //重置io_buf中的内置位置指针
    buffer->length = 0;
    buffer->head = 0;

    pthread_mutex_lock(&_mutex);

    //断言，一定可以在_pool的key中找到index
    // 等价于使用if(_pool.find(index) != _pool.end())
    assert(_pool.find(index) != _pool.end());

    //将buffer插回链表头部
    buffer->next = _pool[index];
    _pool[index] = buffer;

    pthread_mutex_unlock(&_mutex);
}